#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

#include <unordered_map>
#include "util.hpp"
#include "level.hpp"
#include "Format.hpp"
#include "sink.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include "looper.hpp"

class Logger
{
protected:
    mutex _mutex;
    string _logger_name;
    atomic<LogLevel::value> _limit_level;
    Formatter::ptr _formatter;
    vector<LogSink::ptr> _sink;
    virtual void log(const char *data, size_t len) = 0;

public:
    using ptr = shared_ptr<Logger>;
    Logger(const string &logger_name,
           LogLevel::value level,
           Formatter::ptr &formatter,
           vector<LogSink::ptr> &sinks) : _logger_name(logger_name), _limit_level(level), _formatter(formatter), _sink(sinks)
    {
    }
    const string& name()
    {
        return _logger_name;
    }
    void debug(const string &file, size_t line, const string &fmt, ...)
    {
        if (LogLevel::value::Debug < _limit_level)
            return;
        va_list al;
        va_start(al, fmt); // 让al指向n参数之后的第一个可变参数
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), al);
        if (ret == -1)
        {
            cout << "vasprintf failed!" << endl;
            return;
        }
        va_end(al); // 清空可变参数列表，就是将al置空
        serialize(LogLevel::value::Debug, file, line, res);
    }
    void info(const string &file, size_t line, const string &fmt, ...)
    {
        if (LogLevel::value::Info < _limit_level)
            return;
        va_list al;
        va_start(al, fmt); // 让al指向n参数之后的第一个可变参数
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), al);
        if (ret == -1)
        {
            cout << "vasprintf failed!" << endl;
            return;
        }
        va_end(al); // 清空可变参数列表，就是将al置空
        serialize(LogLevel::value::Info, file, line, res);
    }
    void warn(const string &file, size_t line, const string &fmt, ...)
    {
        if (LogLevel::value::Warn < _limit_level)
            return;
        va_list al;
        va_start(al, fmt); // 让al指向n参数之后的第一个可变参数
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), al);
        if (ret == -1)
        {
            cout << "vasprintf failed!" << endl;
            return;
        }
        va_end(al); // 清空可变参数列表，就是将al置空
        serialize(LogLevel::value::Warn, file, line, res);
    }
    void error(const string &file, size_t line, const string &fmt, ...)
    {
        if (LogLevel::value::Error < _limit_level)
            return;
        va_list al;
        va_start(al, fmt); // 让al指向n参数之后的第一个可变参数
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), al);
        if (ret == -1)
        {
            cout << "vasprintf failed!" << endl;
            return;
        }
        va_end(al); // 清空可变参数列表，就是将al置空
        serialize(LogLevel::value::Error, file, line, res);
    }
    void fatal(const string &file, size_t line, const string &fmt, ...)
    {
        if (LogLevel::value::Fatal < _limit_level)
            return;
        va_list al;
        va_start(al, fmt); // 让al指向n参数之后的第一个可变参数
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), al);
        if (ret == -1)
        {
            cout << "vasprintf failed!" << endl;
            return;
        }
        va_end(al); // 清空可变参数列表，就是将al置空
        serialize(LogLevel::value::Fatal, file, line, res);
    }

protected:
    void serialize(LogLevel::value level, const string &file, size_t line, char *str)
    {
        LogMsg msg(_logger_name, file, line, str, level);
        stringstream ss;
        _formatter->format(ss, msg);
        log(ss.str().c_str(), ss.str().size());
        free(str);
    }
};

class SynLogger : public Logger
{
public:
    SynLogger(const string &logger_name,
              LogLevel::value level,
              Formatter::ptr &formatter,
              vector<LogSink::ptr> &sinks) : Logger(logger_name, level, formatter, sinks)
    {
    }

protected:
    void log(const char *data, size_t len)
    {
        unique_lock<mutex> lock(_mutex);
        if (_sink.empty())
        {
            return;
        }
        for (auto &sink : _sink)
        {
            sink->log(data, len);
        }
    }
};

enum class LoggerType
{
    LOGGER_SYNC,
    LOGGER_ASYNC
};

class AsyncLogger : public Logger
{
private:
    AsyncLooper::ptr _looper;

public:
    AsyncLogger(const string &logger_name,
                LogLevel::value level,
                Formatter::ptr &formatter,
                vector<LogSink::ptr> &sinks) : Logger(logger_name, level, formatter, sinks), _looper(make_shared<AsyncLooper>(bind(&AsyncLogger::realLog, this, placeholders::_1))) {}

    void log(const char *data, size_t len)
    {
        _looper->push(data, len);
    }

    void realLog(Buffer &buf)
    {
        if (_sink.empty())
            return;
        for (auto &sink : _sink)
        {
            sink->log(buf.begin(), buf.readAbleSize());
        }
    }
};

class LoggerBuilder
{
protected:
    AsyncType _looper_type;
    LoggerType _logger_type;
    string _logger_name;
    LogLevel::value _limit_level;
    Formatter::ptr _formatter;
    vector<LogSink::ptr> _sinks;

public:
    LoggerBuilder() : _logger_type(LoggerType::LOGGER_SYNC), _looper_type(AsyncType::ASYNC_SAFE), _limit_level(LogLevel::value::Debug) {}
    void buildLoggerType(LoggerType type)
    {
        _logger_type = type;
    }
    void buildLoggerName(const string &name)
    {
        _logger_name = name;
    }
    void buildLoggerLevel(LogLevel::value level)
    {
        _limit_level = level;
    }
    void buildEnableUnSafeAsync() { _looper_type = AsyncType::ASYNC_UNSAFE; }
    void buildFormatter(const string &pattern)
    {
        _formatter = make_shared<Formatter>(pattern);
    }
    template <typename SinkType, typename... Args>
    void buildSink(Args &&...args)
    {
        LogSink::ptr psink = SinkFactory::create<SinkType>(forward<Args>(args)...);
        _sinks.push_back(psink);
    }
    virtual Logger::ptr build() = 0;
};

class LocalLoggerBuilder : public LoggerBuilder
{
public:
    Logger::ptr build() override
    {
        assert(_logger_name.empty() == false);
        if (_formatter.get() == nullptr)
        {
            _formatter = make_shared<Formatter>();
        }
        if (_sinks.empty())
        {
            buildSink<StdoutSink>();
        }
        if (_logger_type == LoggerType::LOGGER_ASYNC)
        {
            return make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
        return make_shared<SynLogger>(_logger_name, _limit_level, _formatter, _sinks);
    }
};


class LoggerManager
{
private:
    mutex _mutex;
    Logger::ptr _root_logger;
    unordered_map<string, Logger::ptr> _loggers;
    LoggerManager() {
        unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
        builder->buildLoggerName("root");
        _root_logger = builder->build();
        _loggers.insert({"root",_root_logger});
    }
public:
    static LoggerManager& getInstance()
    {
        static LoggerManager eton;
        return eton;
    }
    void addLogger(Logger::ptr& logger)
    {
        if(hasLogger(logger->name()));
        unique_lock<mutex> lock(_mutex);
        _loggers.insert({logger->name(),logger});
    }
    bool hasLogger(const string& name)
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _loggers.find(name);
        if(it!=_loggers.end())
        {
            return false;
        }
        return true;
    }
    Logger::ptr getLogger(const string& name)
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _loggers.find(name);
        if(it==_loggers.end())
        {
            return Logger::ptr();
        }
        return it->second;
    }
    Logger::ptr rootLogger()
    {
        return _root_logger;
    }
};


//给日志器管理者添加日志器，不需要创建管理者对象添加
class GlobalLoggerBuilder : public LoggerBuilder
{
public:
    Logger::ptr build() override
    {
        assert(_logger_name.empty() == false);
        if (_formatter.get() == nullptr)
        {
            _formatter = make_shared<Formatter>();
        }
        if (_sinks.empty())
        {
            buildSink<StdoutSink>();
        }
        Logger::ptr logger;
        if (_logger_type == LoggerType::LOGGER_ASYNC)
        {
            logger =  make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }else
        {
            logger = make_shared<SynLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
        LoggerManager::getInstance().addLogger(logger);
        return logger;
    }
};


#endif